package com.nbsaas.boot;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Objects;

/**
 * 基于 Feistel 网络的 Long <-> String 可逆混淆实现。
 * 输出采用 Base62（短且 URL 友好）。
 *
 * 使用示例：
 *   LongHandle h = new FeistelLongHandle("my-secret-key");
 *   String s = h.encode(123456789L);
 *   Long x = h.decode(s);
 */
public class FeistelLongHandle implements LongHandle {

    private static final int DEFAULT_ROUNDS = 12;
    private final int rounds;
    private final int[] roundKeys; // 每轮一个 32-bit round key

    /**
     * 构造函数，使用默认轮数。
     * @param key 密钥字符串（任意长度）
     */
    public FeistelLongHandle(String key) {
        this(key, DEFAULT_ROUNDS);
    }

    /**
     * 构造函数，允许指定轮数（建议 8..16）
     * @param key 密钥字符串
     * @param rounds 轮数（大于等于 4）
     */
    public FeistelLongHandle(String key, int rounds) {
        Objects.requireNonNull(key, "key 不能为空");
        if (rounds < 4) throw new IllegalArgumentException("rounds too small");
        this.rounds = rounds;
        this.roundKeys = deriveRoundKeys(key, rounds);
    }

    @Override
    public Long decode(String s) {
        if (s == null) return null;
        try {
            long v = Base62.decodeToLong(s);
            int left = (int) (v >>> 32);
            int right = (int) v;
            // Feistel 解密：从最后一轮到第一轮
            for (int round = rounds - 1; round >= 0; round--) {
                int k = roundKeys[round];
                int newRight = left;
                int newLeft = right ^ f(newRight, k);
                left = newLeft;
                right = newRight;
            }
            long orig = (((long) left) << 32) | ((right & 0xFFFFFFFFL));
            return orig;
        } catch (Exception ex) {
            // 解码失败或格式错误返回 null（或按需抛出）
            return null;
        }
    }

    @Override
    public String encode(Long num) {
        if (num == null) return null;
        long value = num;
        int left = (int) (value >>> 32);
        int right = (int) value;
        // Feistel 加密：从第一轮到最后一轮
        for (int round = 0; round < rounds; round++) {
            int k = roundKeys[round];
            int newLeft = right;
            int newRight = left ^ f(newLeft, k);
            left = newLeft;
            right = newRight;
        }
        long combined = (((long) left) << 32) | ((right & 0xFFFFFFFFL));
        return Base62.encodeLong(combined);
    }

    // -------- Feistel 辅助函数 --------

    /**
     * 非线性轮函数 F：输入 32-bit 半块和 32-bit round-key 输出 32-bit。
     * 这是一个混合操作（xor -> mul -> rot -> xor），设计时注意性能与混淆度平衡。
     */
    private static int f(int halfBlock, int roundKey) {
        // 使用无符号运算语义（用 long 做中间以防 Java 符号扩展影响）
        long x = Integer.toUnsignedLong(halfBlock);
        long k = Integer.toUnsignedLong(roundKey);

        // 非线性混合常数（黄金分割常数等）
        final long C1 = 0x9E3779B97F4A7C15L; // 64-bit 常数用于混淆（部分混用）
        // 先 xor，再乘以常数，再旋转，再 xor roundKey 的低位
        long t = (x ^ k);
        // 乘以一个奇数常数（在 64-bit 下再取低 32 位）
        t = (t * 0x9E3779B1L) & 0xFFFFFFFFL; // 0x9E3779B1 是 32-bit 的黄金乘子
        int t32 = (int) t;
        // 旋转 + 更多混淆
        int rotated = Integer.rotateLeft(t32, (int) ((k & 31) + 1));
        int out = rotated ^ (int) (C1 & 0xFFFFFFFF) ^ (roundKey + Integer.rotateRight(roundKey, 13));
        return out;
    }

    /**
     * 用 SHA-256 将 key 与轮号派生为 round keys（32-bit each）。
     * 派生时将 key bytes 和轮号字节拼接以得到不同输出。
     */
    private static int[] deriveRoundKeys(String key, int rounds) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
            int[] keys = new int[rounds];
            for (int i = 0; i < rounds; i++) {
                md.reset();
                md.update(keyBytes);
                // 加入轮号和一个固定魔数，避免简单循环产生模式
                md.update((byte) i);
                md.update((byte) 0xA5);
                byte[] digest = md.digest();
                // 取 digest 的前 4 字节作为 32-bit round key（大端）
                int k = ((digest[0] & 0xFF) << 24) | ((digest[1] & 0xFF) << 16)
                        | ((digest[2] & 0xFF) << 8) | (digest[3] & 0xFF);
                // 如 k 为 0，可用一个常数替代以避免 F 函数退化
                if (k == 0) k = 0x9E3779B1;
                keys[i] = k;
            }
            // 洗牌（可选），这里简单返回
            return keys;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // -------- Base62 辅助类（短串、URL 友好） --------
    private static final class Base62 {
        private static final char[] CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
        private static final int BASE = 62;

        static String encodeLong(long value) {
            // 将 unsigned long 当作正数处理
            if (value == 0) return "0";
            // 使用无符号 BigInteger 风格处理负数
            java.math.BigInteger bi = toUnsignedBigInteger(value);
            StringBuilder sb = new StringBuilder();
            java.math.BigInteger base = java.math.BigInteger.valueOf(BASE);
            while (bi.signum() != 0) {
                java.math.BigInteger[] dr = bi.divideAndRemainder(base);
                sb.append(CHARS[dr[1].intValue()]);
                bi = dr[0];
            }
            return sb.reverse().toString();
        }

        static long decodeToLong(String s) {
            java.math.BigInteger bi = java.math.BigInteger.ZERO;
            for (char c : s.toCharArray()) {
                int idx = charIndex(c);
                bi = bi.multiply(java.math.BigInteger.valueOf(BASE)).add(java.math.BigInteger.valueOf(idx));
            }
            // 截断到 64-bit（无符号）
            java.math.BigInteger mask = java.math.BigInteger.ONE.shiftLeft(64).subtract(java.math.BigInteger.ONE);
            bi = bi.and(mask);
            return bi.longValue();
        }

        private static int charIndex(char c) {
            if (c >= '0' && c <= '9') return c - '0';
            if (c >= 'A' && c <= 'Z') return 10 + (c - 'A');
            if (c >= 'a' && c <= 'z') return 36 + (c - 'a');
            throw new IllegalArgumentException("Invalid base62 char: " + c);
        }

        private static java.math.BigInteger toUnsignedBigInteger(long val) {
            if (val >= 0) return java.math.BigInteger.valueOf(val);
            // val negative -> add 2^64
            return java.math.BigInteger.valueOf(val & Long.MAX_VALUE).setBit(63);
        }
    }

    // -------- 简单自测演示 --------
    public static void main(String[] args) {
        FeistelLongHandle h = new FeistelLongHandle("my-super-secret-key-2025", 12);

        long[] tests = new long[] {
                0L,
                1L,
                -1L,
                Long.MAX_VALUE,
                Long.MIN_VALUE,
                1234567890123456789L,
                0xFEDCBA9876543210L
        };

        for (long t : tests) {
            String enc = h.encode(t);
            Long dec = h.decode(enc);
            System.out.printf("orig=%d (0x%016X) -> enc=%s -> dec=%d (ok=%b)%n",
                    t, t, enc, dec, dec != null && dec == t);
        }

        // 简单乱序性测试：连续 10 个值
        System.out.println("连续值测试：");
        FeistelLongHandle h2 = new FeistelLongHandle("another-key", 10);
        for (long i = 0; i < 10; i++) {
            System.out.printf("%d -> %s%n", i, h2.encode(i));
        }
    }
}
